package com.ygg.cps.afx.jdbc ;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.Date;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.SQLXML;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.log4j.Logger;

import com.ygg.cps.afx.constant.CommonConst;
import com.ygg.cps.afx.constant.CommonEnum;
import com.ygg.cps.afx.constant.CommonEnum.FILL_PSTMT_TYPE;
import com.ygg.cps.afx.constant.DBConst;
import com.ygg.cps.afx.exception.DaoException;
import com.ygg.cps.afx.jdbc.intf.BaseDaoIF;
import com.ygg.cps.afx.jdbc.intf.ResultSetHandler;
 

public class BaseDao<T> implements BaseDaoIF<T>
{
    
    private static final  Logger log = Logger.getLogger(BaseDao.class);
    
    protected String connectionPooolkey = "";
    
    // 转成Bean时的属性
    private final Map<String, String> columnToPropertyOverrides;
    
    /**
     * Set a bean's primitive properties to these defaults when SQL NULL is returned. These are the same as the defaults
     * that ResultSet get* methods return in the event of a NULL column.
     */
    private static final Map<Class<?>, Object> primitiveDefaults = new HashMap<Class<?>, Object>();
    
    static
    {
        primitiveDefaults.put(Integer.TYPE, Integer.valueOf(0));
        primitiveDefaults.put(Short.TYPE, Short.valueOf((short)0));
        primitiveDefaults.put(Byte.TYPE, Byte.valueOf((byte)0));
        primitiveDefaults.put(Float.TYPE, Float.valueOf(0f));
        primitiveDefaults.put(Double.TYPE, Double.valueOf(0d));
        primitiveDefaults.put(Long.TYPE, Long.valueOf(0L));
        primitiveDefaults.put(Boolean.TYPE, Boolean.FALSE);
        primitiveDefaults.put(Character.TYPE, Character.valueOf((char)0));
    }
    
    public BaseDao()
    {
        this(new HashMap<String, String>());
    }
    
    public BaseDao(Map<String, String> columnToPropertyOverrides)
    {
        this.setDbConfig(DBConst.C3P0_CONNECTION_POOL);
        if (columnToPropertyOverrides == null)
        {
            throw new IllegalArgumentException("columnToPropertyOverrides map cannot be null");
        }
        this.columnToPropertyOverrides = columnToPropertyOverrides;
    }
    
    /**
     * 子类可重载
     * 
     * @param connectionPooolkey_
     */
    protected void setDbConfig(String connectionPooolkey_)
    {
        if (connectionPooolkey_ == null || connectionPooolkey_.equals(""))
            this.connectionPooolkey = DBConst.C3P0_CONNECTION_POOL;
        else
            this.connectionPooolkey = connectionPooolkey_;
    }
    
    @Override
    public List<T> queryForT(String sql, List<QueryParam> params, Class<T> type)
        throws DaoException
    {
        Connection conn = null;
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        try
        {
            
            conn = ConnectionManager.getConnection(connectionPooolkey);
            pstmt = conn.prepareStatement(sql);
            if (params != null)
            {
                fillPreparedStatement(pstmt, params);
            }
            rs = pstmt.executeQuery();
            return convertResultSet2T(rs, type);
        }
        catch (SQLException e)
        {
            log.error("queryAllInfo出错！");
            throw new DaoException(e);
        }
        finally
        {
            close(rs);
            close(pstmt);
            closeQuery(conn);
            
        }
    }
    
    public List<T> queryForT(String sql, Object[] params, Class<T> type)
        throws DaoException
    {
        List<QueryParam> queryParams = this.generateQueryParams(params);
        return this.queryForT(sql, queryParams, type);
    }
    
    @Override
    public List<T> queryForT(String sql, Class<T> type)
        throws DaoException
    {
        List<QueryParam> queryParams = null;
        return queryForT(sql, queryParams, type);
    }
    
    @Override
    public int execute(String sql, List<QueryParam> params)
        throws DaoException
    {
        int ret = 0;
        Connection conn = null;
        PreparedStatement pstmt = null;
        try
        {
            conn = ConnectionManager.getConnection(connectionPooolkey);
            pstmt = conn.prepareStatement(sql);
            fillPreparedStatement(pstmt, params);
            ret = pstmt.executeUpdate();
        }
        catch (SQLException e)
        {
            log.error("execute出错！");
            throw new DaoException(e);
        }
        finally
        {
            close(pstmt);
            close(conn);
        }
        return ret;
        
    }
    
    public int execute(String sql, Object[] params)
        throws DaoException
    {
        List<QueryParam> queryParams = this.generateQueryParams(params);
        return this.execute(sql, queryParams);
    }
    
    @Override
    public int insert(String sql, List<QueryParam> params)
        throws DaoException
    {
        int ret = 0;
        Connection conn = null;
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        try
        {
            conn = ConnectionManager.getConnection(connectionPooolkey);
            pstmt = conn.prepareStatement(sql);
            fillPreparedStatement(pstmt, params);
            ret = pstmt.executeUpdate();
            if (ret > 0)
            {
                sql = "SELECT LAST_INSERT_ID() AS id";
                pstmt = conn.prepareStatement(sql);
                rs = pstmt.executeQuery();
                if (rs.first())
                {
                    return rs.getInt("id");
                }
            }
        }
        catch (SQLException e)
        {
            log.error("execute出错！");
            throw new DaoException(e);
        }
        finally
        {
            close(rs);
            close(pstmt);
            close(conn);
        }
        return ret;
        
    }
    
    public int insert(String sql, Object[] params)
        throws DaoException
    {
        List<QueryParam> queryParams = this.generateQueryParams(params);
        return this.insert(sql, queryParams);
    }
    
    @Override
    public int[] insertBatch(String sql, List<QueryParam>[] paramArray)
        throws DaoException
    {
        int len = paramArray.length;
        
        int[] ret = new int[len];
        for(int i=0;i<len;i++)
            ret[i]=0;
        
        Connection conn = null;
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        try
        {
            conn = ConnectionManager.getConnection(connectionPooolkey);
            pstmt = conn.prepareStatement(sql);
            for (int i = 0; i < len; i++)
            {
                fillPreparedStatement(pstmt, paramArray[i]);
                pstmt.addBatch();
            }
            ret= pstmt.executeBatch() ;
        }
        catch (SQLException e)
        {
            log.error("execute出错！");
            throw new DaoException(e);
        }
        finally
        {
            close(rs);
            close(pstmt);
            close(conn);
        }
        return ret;
    }
    
    @Override
    public int[] executeBatch(String[] sqlArray, List<QueryParam>[] paramArray)
        throws DaoException
    {
        int[] retArray = new int[sqlArray.length];
        if (sqlArray.length != paramArray.length)
        {
            log.warn("批量执行sql与参数对数量不相等");
            return null;
        }
        else
        {
            for (int i = 0; i < sqlArray.length; i++)
            {
                int ret = 0;
                ret = execute(sqlArray[i], paramArray[i]);
                retArray[i] = ret;
            }
            return retArray;
        }
    }
    
    @Override
    public T queryForOneT(String sql, List<QueryParam> params, Class<T> type)
        throws DaoException
    {
        List<T> lists = this.queryForT(sql, params, type);
        if (lists != null && !lists.isEmpty())
            return lists.get(0);
        
        log.warn("queryForOneT没有匹配记录！sql:" + sql + ",params:" + params + ",classType:" + type);
        return null;
    }
    
    public T queryForOneT(String sql, Object[] params, Class<T> type)
        throws DaoException
    {
        List<QueryParam> queryParams = this.generateQueryParams(params);
        return queryForOneT(sql, queryParams, type);
    }
    
    public T queryForOneT(String sql, Class<T> type)
        throws DaoException
    {
        List<QueryParam> queryParams = null;
        return queryForOneT(sql, queryParams, type);
    }
    
    @Override
    public List<Integer> queryForInteger(String sql, List<QueryParam> params)
        throws DaoException
    {
        List<Integer> resultId = new ArrayList<Integer>();
        Connection conn = null;
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        try
        {
            conn = ConnectionManager.getConnection(connectionPooolkey);
            pstmt = conn.prepareStatement(sql);
            fillPreparedStatement(pstmt, params);
            rs = pstmt.executeQuery();
            while (rs.next())
            {
                resultId.add(rs.getInt("id"));
            }
        }
        catch (SQLException e)
        {
            log.error("queryAllId出错！");
            throw new DaoException(e);
        }
        finally
        {
            close(rs);
            close(pstmt);
            closeQuery(conn);
        }
        return resultId;
    }
    
    public List<Integer> queryForInteger(String sql, Object[] params)
        throws DaoException
    {
        List<QueryParam> queryParams = this.generateQueryParams(params);
        return this.queryForInteger(sql, queryParams);
    }
    
    public List<Integer> queryForInteger(String sql)
        throws DaoException
    {
        List<QueryParam> queryParams = null;
        return this.queryForInteger(sql, queryParams);
    }
    
    @Override
    public int queryOneId(String sql, List<QueryParam> params)
        throws DaoException
    {
        List<Integer> result = queryForInteger(sql, params);
        if (result.size() == 0)
        {
            return CommonConst.ID_NOT_EXIST;
        }
        return result.get(0);
    }
    
    public int queryOneId(String sql, Object[] params)
        throws DaoException
    {
        if (sql == null || sql.equals("") || params == null || params.length == 0)
            return CommonConst.ID_NOT_EXIST;
        
        List<QueryParam> queryParams = generateQueryParams(params);
        
        return queryOneId(sql, queryParams);
    }
    
    public int queryOneId(String sql)
        throws DaoException
    {
        List<QueryParam> queryParams = null;
        return queryOneId(sql, queryParams);
    }
    
    private List<QueryParam> generateQueryParams(Object[] params)
    {
        if (params == null || params.length == 0)
            return null;
        List<QueryParam> queryParams = new ArrayList<QueryParam>();
        for (int i = 0; i < params.length; i++)
        {
            if (params[i] instanceof String)
                queryParams.add(new QueryParam(FILL_PSTMT_TYPE.STRING, params[i]));
            else if (params[i] instanceof Integer)
                queryParams.add(new QueryParam(FILL_PSTMT_TYPE.INT, params[i]));
            else if (params[i] instanceof Byte)
                queryParams.add(new QueryParam(FILL_PSTMT_TYPE.BYTE, params[i]));
            else if (params[i] instanceof Short)
                queryParams.add(new QueryParam(FILL_PSTMT_TYPE.SHORT, params[i]));
            else if (params[i] instanceof Date)
                queryParams.add(new QueryParam(FILL_PSTMT_TYPE.DATE, params[i]));
            else if (params[i] instanceof java.sql.Timestamp)
                queryParams.add(new QueryParam(FILL_PSTMT_TYPE.TIMESTAMP, params[i]));
            else if (params[i] instanceof Long)
                queryParams.add(new QueryParam(FILL_PSTMT_TYPE.LONG, params[i]));
            else if (params[i] instanceof Float)
                queryParams.add(new QueryParam(FILL_PSTMT_TYPE.FLOAT, params[i]));
            else
                queryParams.add(new QueryParam(FILL_PSTMT_TYPE.STRING, params[i].toString()));
        }
        return queryParams;
    }
    
    @Override
    public List<Map<String, Object>> queryForMap(String sql, List<QueryParam> params)
        throws DaoException
    {
        Connection conn = null;
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        try
        {
            conn = ConnectionManager.getConnection(connectionPooolkey);
            pstmt = conn.prepareStatement(sql);
            fillPreparedStatement(pstmt, params);
            rs = pstmt.executeQuery();
            return convertResultSet2Map(rs);
        }
        catch (SQLException e)
        {
            log.error("queryAllInfo出错！");
            throw new DaoException(e);
        }
        finally
        {
            close(rs);
            close(pstmt);
            closeQuery(conn);
        }
    }
    
    public List<Map<String, Object>> queryForMap(String sql, Object[] params)
        throws DaoException
    {
        List<QueryParam> queryParams = generateQueryParams(params);
        return this.queryForMap(sql, queryParams);
    }
    
    public List<Map<String, Object>> queryForMap(String sql)
        throws DaoException
    {
        List<QueryParam> queryParams = null;
        return this.queryForMap(sql, queryParams);
    }
    
    @Override
    public Map<String, Object> queryForOneMap(String sql, List<QueryParam> params)
        throws DaoException
    {
        List<Map<String, Object>> list = this.queryForMap(sql, params);
        if (list == null || list.size() == 0)
        {
            log.warn("queryForOneMap没有匹配记录！sql:" + sql + ",params:" + params);
            return null;
        }
        else
        {
            return list.get(0);
        }
    }
    
    public Map<String, Object> queryForOneMap(String sql, Object[] params)
        throws DaoException
    {
        List<QueryParam> queryParams = generateQueryParams(params);
        return this.queryForOneMap(sql, queryParams);
    }
    
    public Map<String, Object> queryForOneMap(String sql)
        throws DaoException
    {
        List<QueryParam> queryParams = null;
        return this.queryForOneMap(sql, queryParams);
    }
    
    @SuppressWarnings("unchecked")
    @Override
    public List<T>[] queryBatch2T(String[] sqlArray, List<QueryParam>[] paramArray, Class<T> type)
        throws DaoException
    {
        
        List<T>[] rets = new ArrayList[sqlArray.length];
        if (sqlArray.length != paramArray.length)
        {
            log.warn("批量查询sql与参数对数量不相等");
            return null;
        }
        else
        {
            for (int i = 0; i < sqlArray.length; i++)
            {
                String sql = sqlArray[i];
                List<QueryParam> param = paramArray[i];
                List<T> ret = this.queryForT(sql, param, type);
                rets[i] = ret;
            }
            return rets;
        }
    }
    
    @SuppressWarnings("unchecked")
    @Override
    public List<Map<String, Object>>[] queryBatch2Map(String[] sqlArray, List<QueryParam>[] paramArray)
        throws DaoException
    {
        
        List<Map<String, Object>>[] rets = new ArrayList[sqlArray.length];
        if (sqlArray.length != paramArray.length)
        {
            log.warn("批量查询sql与参数对数量不相等");
            return null;
        }
        else
        {
            for (int i = 0; i < sqlArray.length; i++)
            {
                String sql = sqlArray[i];
                List<QueryParam> param = paramArray[i];
                List<Map<String, Object>> ret = this.queryForMap(sql, param);
                rets[i] = ret;
            }
            return rets;
        }
    }
    
    private List<T> convertResultSet2T(ResultSet rs, Class<T> type)
        throws DaoException
    {
        List<T> retList = new ArrayList<T>();
        try
        {
            List<String> columnNameList = new ArrayList<String>();
            ResultSetMetaData meta = rs.getMetaData();
            for (int i = 1; i <= meta.getColumnCount(); i++)
            {
                columnNameList.add(meta.getColumnName(i));
            }
            while (rs.next())
            {
                retList.add(resultSet2Entity(rs, columnNameList, type));
            }
        }
        catch (SQLException e)
        {
            log.error("convertResultSet出错！");
            throw new DaoException(e);
        }
        return retList;
    }
    
    /**
     * 此方法子类可以重载，这里用反射方法去实例化Bean 　　mysql类型要和java类型相对应，如果有不对应的情况，需要子类Dao重载此方法
     * 
     * @param rs
     * @param columnNameList
     * @return
     * @throws SQLException
     */
    protected T resultSet2Entity(ResultSet rs, List<String> columnNameList, Class<T> type)
        throws SQLException
    {
        
        // T bean = this.newInstance(type);
        
        ResultSetMetaData meta = rs.getMetaData();
        
        PropertyDescriptor[] props = this.propertyDescriptors(type);
        int[] columnToProperty = this.mapColumnsToProperties(meta, props);
        
        return this.createBean(rs, type, props, columnToProperty);
        
    }
    
    private List<Map<String, Object>> convertResultSet2Map(ResultSet rs)
        throws DaoException
    {
        // record list
        List<Map<String, Object>> retList = new ArrayList<Map<String, Object>>();
        
        try
        {
            ResultSetMetaData meta = rs.getMetaData();
            
            // column count
            int colCount = meta.getColumnCount();
            
            // each record
            while (rs.next())
            {
                
                Map<String, Object> recordMap = new HashMap<String, Object>();
                
                // each column
                for (int i = 1; i <= colCount; i++)
                {
                    // column name
                    String name = meta.getColumnName(i);
                    // column value
                    Object value = rs.getObject(i);
                    // add column to record
                    recordMap.put(name, value);
                }
                // ad record to list
                retList.add(recordMap);
            }
        }
        catch (SQLException e)
        {
            log.error("convertResultSet出错");
            throw new DaoException(e);
        }
        return retList;
    }
    
    private void fillPreparedStatement(PreparedStatement pstmt, List<QueryParam> params)
        throws DaoException
    {
        try
        {
            if (params != null && params.size() > 0)
            {
                Iterator<QueryParam> it = params.iterator();
                int index = 1;
                while (it.hasNext())
                {
                    QueryParam param = it.next();
                    CommonEnum.FILL_PSTMT_TYPE paramType = param.getType();
                    Object paramValue = param.getValue();
                    if (paramValue == null)
                    {
                        pstmt.setString(index, "");
                    }
                    else if (paramType == CommonEnum.FILL_PSTMT_TYPE.BYTE)
                    {
                        pstmt.setByte(index, (Byte)paramValue);
                    }
                    else if (paramType == CommonEnum.FILL_PSTMT_TYPE.SHORT)
                    {
                        pstmt.setShort(index, (Short)paramValue);
                    }
                    else if (paramType == CommonEnum.FILL_PSTMT_TYPE.INT)
                    {
                        pstmt.setInt(index, (Integer)paramValue);
                    }
                    else if (paramType == CommonEnum.FILL_PSTMT_TYPE.STRING)
                    {
                        pstmt.setString(index, (String)paramValue);
                    }
                    else if (paramType == CommonEnum.FILL_PSTMT_TYPE.DATE)
                    {
                        pstmt.setDate(index, (Date)paramValue);
                    }
                    else if (paramType == CommonEnum.FILL_PSTMT_TYPE.TIMESTAMP)
                    {
                        pstmt.setTimestamp(index, (Timestamp)paramValue);
                    }
                    else if (paramType == CommonEnum.FILL_PSTMT_TYPE.LONG)
                    {
                        pstmt.setLong(index, (Long)paramValue);
                    }
                    else if (paramType == CommonEnum.FILL_PSTMT_TYPE.FLOAT)
                    {
                        pstmt.setFloat(index, (Float)paramValue);
                    }
                    index++;
                }
            }
        }
        catch (SQLException e)
        {
            log.error("fillPreparedStatement出错！");
            throw new DaoException(e);
        }
    }
    
    public void close(Connection conn)
    {
        try
        {
            if (conn != null)
            {
                if (!conn.isClosed() && conn.getAutoCommit())
                {
                    conn.close();
                }
            }
        }
        catch (SQLException e)
        {
            log.warn("conn关闭出错！");
        }
    }
    
    public void closeQuery(Connection conn)
    {
        
        try
        {
            Connection currConn = ConnectionManager.getCurrentConnection(connectionPooolkey);
            /*
             * System.out.println(conn); System.out.println(currConn); System.out.println(conn.hashCode());
             * System.out.println(currConn.hashCode()); System.out.println(conn == currConn);
             */
            if (currConn != null && conn != null && conn == currConn)
            {
            	  close(conn);
            }
            else
                close(conn);// conn & currConn 不是同一个connection ,可以close
            ConnectionManager.releaseConnection();
        }
        catch (SQLException e)
        {
            e.printStackTrace();
        }
    }
    
    public void close(PreparedStatement pstmt)
    {
        try
        {
            if (pstmt != null)
            {
                pstmt.close();
            }
        }
        catch (SQLException e)
        {
            log.warn("pstmt关闭出错！");
        }
    }
    
    public void close(ResultSet rs)
    {
        try
        {
            if (rs != null)
            {
                rs.close();
            }
        }
        catch (SQLException e)
        {
            log.warn("rs关闭出错！");
        }
    }
    
    // ///////////////////////////根据查询的泛型参数转成Bean //////////////////////////////////////////////
    
    /**
     * T转成Bean 利用反射，性能有损失
     * 
     * @param rs
     * @param type
     * @param props
     * @param columnToProperty
     * @return
     * @throws SQLException
     */
    private T createBean(ResultSet rs, Class<T> type, PropertyDescriptor[] props, int[] columnToProperty)
        throws SQLException
    {
        
        T bean = this.newInstance(type);
        
        for (int i = 1; i < columnToProperty.length; i++)
        {
            
            if (columnToProperty[i] == PROPERTY_NOT_FOUND)
            {
                continue;
            }
            
            PropertyDescriptor prop = props[columnToProperty[i]];
            Class<?> propType = prop.getPropertyType();
            
            Object value = null;
            if (propType != null)
            {
                value = this.processColumn(rs, i, propType);
                
                if (value == null && propType.isPrimitive())
                {
                    value = primitiveDefaults.get(propType);
                }
            }
            
            this.callSetter(bean, prop, value);
        }
        
        return bean;
    }
    
    private T newInstance(Class<T> c)
        throws SQLException
    {
        try
        {
            return c.newInstance();
            
        }
        catch (InstantiationException e)
        {
            throw new SQLException("Cannot create " + c.getName() + ": " + e.getMessage());
            
        }
        catch (IllegalAccessException e)
        {
            throw new SQLException("Cannot create " + c.getName() + ": " + e.getMessage());
        }
    }
    
    private PropertyDescriptor[] propertyDescriptors(Class<?> c)
        throws SQLException
    {
        // Introspector caches BeanInfo classes for better performance
        BeanInfo beanInfo = null;
        try
        {
            beanInfo = Introspector.getBeanInfo(c);
            
        }
        catch (IntrospectionException e)
        {
            throw new SQLException("Bean introspection failed: " + e.getMessage());
        }
        
        return beanInfo.getPropertyDescriptors();
    }
    
    protected int[] mapColumnsToProperties(ResultSetMetaData rsmd, PropertyDescriptor[] props)
        throws SQLException
    {
        
        int cols = rsmd.getColumnCount();
        int[] columnToProperty = new int[cols + 1];
        
        for (int col = 1; col <= cols; col++)
        {
            String columnName = rsmd.getColumnLabel(col);
            if (null == columnName || 0 == columnName.length())
            {
                columnName = rsmd.getColumnName(col);
            }
            String propertyName = columnToPropertyOverrides.get(columnName);
            if (propertyName == null)
            {
                propertyName = columnName;
            }
            for (int i = 0; i < props.length; i++)
            {
                
                if (propertyName.equalsIgnoreCase(props[i].getName()))
                {
                    columnToProperty[col] = i;
                    break;
                }
            }
        }
        
        return columnToProperty;
    }
    
    protected Object processColumn(ResultSet rs, int index, Class<?> propType)
        throws SQLException
    {
        
        if (!propType.isPrimitive() && rs.getObject(index) == null)
        {
            return null;
        }
        
        if (propType.equals(String.class))
        {
            return rs.getString(index);
            
        }
        else if (propType.equals(Integer.TYPE) || propType.equals(Integer.class))
        {
            return Integer.valueOf(rs.getInt(index));
            
        }
        else if (propType.equals(Boolean.TYPE) || propType.equals(Boolean.class))
        {
            return Boolean.valueOf(rs.getBoolean(index));
            
        }
        else if (propType.equals(Long.TYPE) || propType.equals(Long.class))
        {
            return Long.valueOf(rs.getLong(index));
            
        }
        else if (propType.equals(Double.TYPE) || propType.equals(Double.class))
        {
            return Double.valueOf(rs.getDouble(index));
            
        }
        else if (propType.equals(Float.TYPE) || propType.equals(Float.class))
        {
            return Float.valueOf(rs.getFloat(index));
            
        }
        else if (propType.equals(Short.TYPE) || propType.equals(Short.class))
        {
            return Short.valueOf(rs.getShort(index));
            
        }
        else if (propType.equals(Byte.TYPE) || propType.equals(Byte.class))
        {
            return Byte.valueOf(rs.getByte(index));
            
        }
        else if (propType.equals(Timestamp.class))
        {
            return rs.getTimestamp(index);
            
        }
        else if (propType.equals(SQLXML.class))
        {
            return rs.getSQLXML(index);
            
        }
        else
        {
            return rs.getObject(index);
        }
        
    }
    
    @SuppressWarnings("unchecked")
    private void callSetter(Object target, PropertyDescriptor prop, Object value)
        throws SQLException
    {
        
        Method setter = prop.getWriteMethod();
        
        if (setter == null)
        {
            return;
        }
        
        Class<?>[] params = setter.getParameterTypes();
        try
        {
            // convert types for some popular ones
            if (value instanceof java.util.Date)
            {
                final String targetType = params[0].getName();
                if ("java.sql.Date".equals(targetType))
                {
                    value = new java.sql.Date(((java.util.Date)value).getTime());
                }
                else if ("java.sql.Time".equals(targetType))
                {
                    value = new java.sql.Time(((java.util.Date)value).getTime());
                }
                else if ("java.sql.Timestamp".equals(targetType))
                {
                    Timestamp tsValue = (Timestamp)value;
                    int nanos = tsValue.getNanos();
                    value = new java.sql.Timestamp(tsValue.getTime());
                    ((Timestamp)value).setNanos(nanos);
                }
            }
            else if (value instanceof String && params[0].isEnum())
            {
                value = Enum.valueOf(params[0].asSubclass(Enum.class), (String)value);
            }
            
            // Don't call setter if the value object isn't the right type
            if (this.isCompatibleType(value, params[0]))
            {
                setter.invoke(target, new Object[] {value});
            }
            else
            {
                throw new SQLException("Cannot set " + prop.getName() + ": incompatible types, cannot convert " + value.getClass().getName() + " to " + params[0].getName());
                // value cannot be null here because isCompatibleType allows null
            }
            
        }
        catch (IllegalArgumentException e)
        {
            throw new SQLException("Cannot set " + prop.getName() + ": " + e.getMessage());
            
        }
        catch (IllegalAccessException e)
        {
            throw new SQLException("Cannot set " + prop.getName() + ": " + e.getMessage());
            
        }
        catch (InvocationTargetException e)
        {
            throw new SQLException("Cannot set " + prop.getName() + ": " + e.getMessage());
        }
    }
    
    private boolean isCompatibleType(Object value, Class<?> type)
    {
        // Do object check first, then primitives
        if (value == null || type.isInstance(value))
        {
            return true;
            
        }
        else if (type.equals(Integer.TYPE) && value instanceof Integer)
        {
            return true;
            
        }
        else if (type.equals(Long.TYPE) && value instanceof Long)
        {
            return true;
            
        }
        else if (type.equals(Double.TYPE) && value instanceof Double)
        {
            return true;
            
        }
        else if (type.equals(Float.TYPE) && value instanceof Float)
        {
            return true;
            
        }
        else if (type.equals(Short.TYPE) && value instanceof Short)
        {
            return true;
            
        }
        else if (type.equals(Byte.TYPE) && value instanceof Byte)
        {
            return true;
            
        }
        else if (type.equals(Character.TYPE) && value instanceof Character)
        {
            return true;
            
        }
        else if (type.equals(Boolean.TYPE) && value instanceof Boolean)
        {
            return true;
            
        }
        return false;
        
    }
    
    protected static final int PROPERTY_NOT_FOUND = -1;
    
    @Override
    public T queryForBean(String sql, Object[] params, Class<T> type)
        throws DaoException
    {
        return queryForBean(sql, params, type, null);
    }
    
    public T queryForBean(String sql, Object params[], ResultSetHandler rsh)
        throws DaoException
    {
        return queryForBean(sql, params, null, rsh);
    }
    
    @SuppressWarnings("unchecked")
    private T queryForBean(String sql, Object params[], Class<T> type, ResultSetHandler rsh)
        throws DaoException
    {
        /*if (sql == null || sql.equals(""))
            return null;
        if (type == null && rsh == null)
            return null;*/
        Connection conn = null;
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        try
        {
            conn = ConnectionManager.getConnection(connectionPooolkey);
            pstmt = conn.prepareStatement(sql);
            List<QueryParam> queryParams = this.generateQueryParams(params);
            fillPreparedStatement(pstmt, queryParams);
            rs = pstmt.executeQuery();
            if (rsh == null && type != null)
                rsh = new BeanHandler(type);
            
            return (T)rsh.handler(rs);
        }
        catch (SQLException e)
        {
            log.error("queryForBean出错！");
            throw new DaoException(e);
        }
        finally
        {
            close(rs);
            close(pstmt);
            closeQuery(conn);
        }
    }
    
    @Override
    public List<T> queryForBeans(String sql, Object[] params, Class<T> type)
        throws DaoException
    {
        
        return queryForBeans(sql, params, type, null);
    }
    
    public List<T> queryForBeans(String sql, Object params[], ResultSetHandler rsh)
        throws DaoException
    {
        return queryForBeans(sql, params, null, rsh);
    }
    
    @SuppressWarnings("unchecked")
    private List<T> queryForBeans(String sql, Object params[], Class<T> type, ResultSetHandler rsh)
        throws DaoException
    {
        /*if (sql == null || sql.equals(""))
            return null;
        if (type == null && rsh == null)
            return null;*/
        Connection conn = null;
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        try
        {
            conn = ConnectionManager.getConnection(connectionPooolkey);
            pstmt = conn.prepareStatement(sql);
            List<QueryParam> queryParams = this.generateQueryParams(params);
            fillPreparedStatement(pstmt, queryParams);
            rs = pstmt.executeQuery();
            if (rsh == null && type != null)
                rsh = new BeanListHandler(type);
            
            return (List<T>)rsh.handler(rs);
        }
        catch (SQLException e)
        {
            log.error("queryForBeans出错！");
            throw new DaoException(e);
        }
        finally
        {
            close(rs);
            close(pstmt);
            closeQuery(conn);
        }
    }
    
}
